package com.example.spider.util;

import cn.hutool.core.io.FileUtil;
import cn.hutool.crypto.digest.DigestUtil;
import org.shoulder.core.util.ByteUtils;
import org.shoulder.core.util.StringUtils;

import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardCopyOption;
import java.nio.file.StandardOpenOption;
import java.util.Arrays;

/**
 * util
 */
public class PathUtil {

    public static void main(String[] args) throws IOException {
        System.out.println("start");
        long s = System.currentTimeMillis();
//        copyOverrideIfNotEqual("E:\\vedio_book\\random\\mdyy72_topmdyy_113642_0.ts", "E:\\vedio_book\\random\\mdyy72_topmdyy_113642_0.ts.xx");
        Files.move(Path.of("E:\\vedio_book\\random\\mdyy72_topmdyy_113642_0.ts"), Path.of("E:\\vedio_book\\random\\mdyy72_topmdyy_113642_0.ts.xxx"), StandardCopyOption.ATOMIC_MOVE, StandardCopyOption.REPLACE_EXISTING);
        System.out.println("end " + (System.currentTimeMillis() - s));
//        byte[] n = readLastNBytes("E:\\vedio_book\\random\\mdyy72_topmdyy_113642_0.ts", 1000);

    }
    /**
     * 覆盖方式写入
     */
    public static void rewriteFile(String content, Path path) {
        try {
            if (Files.notExists(path)) {
                ensureExist(path.getParent());
                Files.createFile(path);
            }
            Files.writeString(path, content, StandardOpenOption.WRITE);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }


    public static void copyOverrideIfNotEqual(String source, String destFile) {
        // 幂等检查【MD5：文件大小 or md5】
        if (!fastCompare(source, destFile)) {
            try {
                // move
                Files.copy(Path.of(source), Path.of(destFile), StandardCopyOption.REPLACE_EXISTING);
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }
    }

    public static boolean fastCompare(String file, String file2) {
        return StringUtils.equals(file, file2) || Arrays.equals(fastMd5(file), fastMd5(file2));
    }

    public static byte[] fastMd5(String file) {
        long kb60 = 1024 * 60;
        long kb200 = 1024 * 200;
        long mb1 = 1024 * 1000;
        long mb5 = mb1 * 5;
        File targetFile = new File(file);
        if (!targetFile.exists()) {
            return null;
        }
        long fileLength = targetFile.length();
        if (fileLength < kb200) {
            // 文件大小 + 完整md5
            return ByteUtils.compound(Arrays.asList(
                    ByteUtils.toBytes(fileLength),
                    DigestUtil.md5(targetFile))
            );
        }
        if (fileLength < mb5) {
            // 文件大小 + 中间段md5
            return ByteUtils.compound(Arrays.asList(
                    DigestUtil.md5(ByteUtils.toBytes(fileLength)),
                    DigestUtil.md5(readMidNBytes(file, kb60)))
            );
        }
        // 文件大小 + md5(靠前一段) + md5(中间一段) + md5(靠后一段)
        return ByteUtils.compound(Arrays.asList(
                ByteUtils.toBytes(fileLength),
                DigestUtil.md5(readStartNBytes(file, kb60, mb1)),
                DigestUtil.md5(readMidNBytes(file, kb60)),
                DigestUtil.md5(readLastNBytesIgnore(file, kb60, mb1)))
        );
    }

    public static byte[] readStartNBytes(String filePath, long n, long ignore) {
        try (RandomAccessFile randomAccessFile = new RandomAccessFile(filePath, "r")) {
            long fileLength = randomAccessFile.length();
            long pos = 0;
            long canIgnoreLength = fileLength - n;
            if (canIgnoreLength <= 0) {
                // 最多只能读这些了
                n = fileLength;
            } else {
                pos = Math.min(canIgnoreLength, ignore);
            }
            byte[] result = new byte[(int) n];
            randomAccessFile.seek(pos);
            int readN = randomAccessFile.read(result);
            return result;
        } catch (Exception e) {
            throw new RuntimeException(e);
        }

    }

    public static byte[] readMidNBytes(String filePath, long n) {
        try (RandomAccessFile randomAccessFile = new RandomAccessFile(filePath, "r")) {
            long fileLength = randomAccessFile.length();
            long pos = fileLength - 1 - n;
            if (pos < 0) {
                pos = 0;
                // 最多只能读这些了
                n = fileLength;
            } else {
                pos -= (fileLength - n) / 2;
            }
            randomAccessFile.seek(pos);
            byte[] result = new byte[(int) n];
            int readN = randomAccessFile.read(result);
            return result;
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    public static byte[] readLastNBytesIgnore(String filePath, long n, long ignore) {
        try (RandomAccessFile randomAccessFile = new RandomAccessFile(filePath, "r")) {
            long fileLength = randomAccessFile.length();
            long pos = fileLength - 1 - n;
            if (pos < 0) {
                pos = 0;
                // 最多只能读这些了
                n = fileLength;
            }
            if (pos <= ignore) {
                // 再忽略就没了
                ignore = ignore - pos;
            }
            pos -= ignore;
            randomAccessFile.seek(pos);
            byte[] result = new byte[(int) n];
            int readN = randomAccessFile.read(result);
            return result;
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    public static byte[] readLastNBytes(String filePath, long n) {
        return readLastNBytesIgnore(filePath, n, 0);
    }

    /**
     * 覆盖式写入，且保证写入完整，不会有写一半的情况，否则是临时文件，自动删除临时文件
     * 【适用：写小文件】
     *
     * @param content 写入内容
     * @param path    路径
     */
    public static void writeWithTransaction(byte[] content, Path path) {
        try {
            String tempFileName = path.toString() + ".tmp";
            Path tempPath = Path.of(tempFileName);
            if (Files.notExists(tempPath)) {
                ensureExist(tempPath.getParent());
                Files.createFile(tempPath);
            }
            Files.write(tempPath, content, StandardOpenOption.WRITE);
            Files.move(tempPath, path, StandardCopyOption.ATOMIC_MOVE);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 覆盖方式写入
     */
    public static void rewriteFile(byte[] content, Path path) {
        try {
            if (Files.notExists(path)) {
                ensureExist(path.getParent());
                Files.createFile(path);
            }
            Files.write(path, content, StandardOpenOption.WRITE);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 确保路径
     */
    public static void ensureExist(Path dirPath) {
        FileUtil.mkdir(dirPath);
    }

}
